package com.novel.framework.redis.impl;

import com.novel.common.exception.business.BusinessException;
import com.novel.common.utils.Assert;
import com.novel.common.utils.model.Cache;
import com.novel.framework.redis.ICacheService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.cache.CacheType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis 服务实现类
 *
 * @author novel
 * @since 2019/6/4
 */
@Component
@SuppressWarnings("unchecked")
public class RedisServiceImpl implements ICacheService, com.novel.common.cache.service.ICacheService {
    @Value("${spring.redis.host}")
    private String ip;
    @Resource
    private  RedisTemplate redisTemplate;

    /**
     * 写入缓存
     *
     * @param key   key
     * @param value value
     * @return 是否成功
     */
    @Override
    public boolean set(final String key, Object value) {
        Assert.isNotNull(key);
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
        return true;
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key   key
     * @param value value
     * @return 是否成功
     */
    @Override
    public boolean set(final String key, Object value, Long expireTime) {
        return set(key, value, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 读取缓存
     *
     * @param key        键
     * @param value      值
     * @param expireTime 超时时间
     * @param unit       单位
     * @return 缓存对象
     */
    @Override
    public boolean set(String key, Object value, Long expireTime, TimeUnit unit) {
        Assert.isNotNull(key);
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, unit);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 批量删除对应的value
     *
     * @param keys 删除的key
     */
    @Override
    public void remove(final String... keys) {
        for (String key : keys) {
            Assert.isNotNull(key);
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern 删除的正则表达式
     */
    @Override
    public void removePattern(final String pattern) {
        Assert.isNotNull(pattern);
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (Objects.nonNull(keys) && keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key key
     */
    @Override
    public void remove(final String key) {
        Assert.isNotNull(key);
        if (exists(key)) {
            redisTemplate.delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key key
     * @return 是否存在
     */
    @Override
    public boolean exists(final String key) {
        Assert.isNotNull(key);
        return Optional.ofNullable(redisTemplate.hasKey(key)).orElse(false);
    }

    /**
     * 读取缓存
     *
     * @param key key
     * @return value
     */
    @Override
    public <T> T get(final String key) {
        Assert.isNotNull(key);
        ValueOperations<Serializable, T> operations = redisTemplate.opsForValue();
        return operations.get(key);
    }

    /**
     * 清空redis
     */
    @Override
    public void clear() {
        Set<Object> keys = redisTemplate.keys("*");
        if (Objects.nonNull(keys) && keys.size() > 0) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 获取所有key
     *
     * @param pattern 正则表达式
     * @return key集合
     */
    @Override
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 批量获取
     *
     * @param <T>  结果泛型
     * @param keys 匹配字符
     * @return 结果
     */
    @Override
    public <T> List<T> multiGet(Set<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 获取缓存的服务信息
     *
     * @return 缓存信息
     */
    @Override
    public Map<Object, Object> getServerInfo() {
        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
        RedisConnection conn = null;
        try {
            if (Objects.nonNull(factory)) {
                conn = RedisConnectionUtils.getConnection(factory);
                Long dbSize = conn.dbSize();
                Properties info = conn.info();
                if (Objects.nonNull(info)) {
                    info.put("dbSize", dbSize);
                }
                return info;
            }
            return new Hashtable<>();
        } finally {
            if (Objects.nonNull(factory)) {
                RedisConnectionUtils.releaseConnection(conn, factory);
            }
        }
    }

    /**
     * 哈希 添加
     *
     * @param key     键
     * @param hashKey 哈希键
     * @param value   值
     */
    @Override
    public void hmSet(String key, Object hashKey, Object value) {
        Assert.isNotNull(key);
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key, hashKey, value);
    }

    /**
     * 哈希获取数据
     *
     * @param key     键
     * @param hashKey 哈希键
     * @return 值
     */
    @Override
    public Object hmGet(String key, Object hashKey) {
        Assert.isNotNull(key);
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 列表添加
     *
     * @param k 键
     * @param v 值
     */
    @Override
    public void lPush(String k, Object v) {
        Assert.isNotNull(k);
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k, v);
    }

    /**
     * 列表获取
     *
     * @param k  键
     * @param l  获取长度
     * @param l1 第几个开始
     * @return 值
     */
    @Override
    public List<Object> lRange(String k, long l, long l1) {
        Assert.isNotNull(k);
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }

    /**
     * 集合添加
     *
     * @param key   键
     * @param value 值
     */
    @Override
    public void add(String key, Object value) {
        Assert.isNotNull(key);
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key, value);
    }

    /**
     * 集合获取
     *
     * @param key 键
     * @return 值
     */
    @Override
    public Set<Object> setMembers(String key) {
        Assert.isNotNull(key);
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     *
     * @param key    键
     * @param value  值
     * @param source 信息
     */
    @Override

    public void zAdd(String key, Object value, double source) {
        Assert.isNotNull(key);
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key, value, source);
    }

    /**
     * 有序集合获取
     *
     * @param key     键
     * @param source  信息
     * @param source1 信息
     * @return 值
     */
    @Override
    public Set<Object> rangeByScore(String key, double source, double source1) {
        Assert.isNotNull(key);
        ZSetOperations<String, Object> zSet = redisTemplate.opsForZSet();
        return zSet.rangeByScore(key, source, source1);
    }

    /**
     * 获取缓存信息
     *
     * @return 缓存信息
     */
    @Override
    public Cache getCacheInfo() {
        Map<Object, Object> info = getServerInfo();
        Cache cache = new Cache();
        cache.setType(CacheType.REDIS);
        cache.setOs(String.valueOf(info.get("os")));
        cache.setVersion(String.valueOf(info.get("redis_version")));
        cache.setPort(Integer.valueOf(info.get("tcp_port").toString()));
        cache.setUptimeInSeconds(Long.parseLong(info.get("uptime_in_seconds").toString()));
        cache.setConnectedClients(Integer.parseInt(info.get("connected_clients").toString()));
        cache.setUsed(Long.parseLong(info.get("used_memory").toString()));
        cache.setTotal(Long.parseLong(info.get("total_system_memory").toString()));
        cache.setDbSize(Long.parseLong(info.get("dbSize").toString()));
        cache.setUsedCpu(Double.parseDouble(info.get("used_cpu_user_children").toString()));
        cache.setMode(info.get("redis_mode").toString());
        cache.setIp(ip);
        return cache;
    }
}
